;***************************************************************************
;*                    Coleco ADAM Keyboard 6801 ROM                        *
;*            assembly listing regenerated by Chris Braymen                *
;*                minor additions by Richard F. Drushel                    *
;***************************************************************************

;***********************************************************
;* 6801 port registers
;***********************************************************

P1_DIR          EQU     $0000
P2_DIR          EQU     $0001
P1_DATA         EQU     $0002
P2_DATA         EQU     $0003
P3_DIR          EQU     $0004
P4_DIR          EQU     $0005
P3_DATA         EQU     $0006
P4_DATA         EQU     $0007
T_CNTLSTAT      EQU     $0008
COUNTER_HI      EQU     $0009
COUNTER_LO      EQU     $000A
OUT_COMPARE     EQU     $000B
RAM_CNTL        EQU     $0014

;***********************************************************
;* 6801 serial port registers
;***********************************************************

SCI_RM          EQU     10H             ;rate and mode
SCI_TR_CS       EQU     11H             ;xmit control and status
SCI_RX          EQU     12H             ;serial receive register
SCI_TX          EQU     13H             ;serial transmit register

ORFE            EQU     40H             ;overflow error bit
TDRE_MASK       EQU     20H             ;ready to xmit bit

;**********************************************************
;* Keyboard states
;**********************************************************

CNTRL           EQU     00H
LENGTH_HI       EQU     01H
LENGTH_LO       EQU     02H
DATAIN          EQU     03H
		EQU     04H
STATUS_OUT      EQU     05H
STAT_CS_OUT     EQU     06H
S_ACK_IN        EQU     07H
LEN_HI_OUT      EQU     08H
LEN_LO_OUT      EQU     09H
DATA_OUT        EQU     0AH
CS_OUT          EQU     0BH
ACK_IN          EQU     0CH

;**********************************************************************
;* Network commands and responses
;**********************************************************************

KB_NODE  EQU     0001H          ;ADAMnet device 1=keyboard

MN_RESET        EQU     00H*16
MN_STATUS       EQU     01H*16
MN_ACK          EQU     02H*16
MN_CLR          EQU     03H*16
MN_RECEIVE      EQU     04H*16
MN_CANCEL       EQU     05H*16
MN_SEND         EQU     06H*16
MN_NACK         EQU     07H*16
MN_READY        EQU     0DH*16

NM_STATUS       EQU     08H*16
NM_ACK          EQU     09H*16
NM_CANCEL       EQU     0AH*16
NM_SEND         EQU     0BH*16
NM_NACK         EQU     0CH*16

TIMER_VALUE     EQU     03B2H   ;946
HOME            EQU     0080H   ;ASCII home key

		org     $F800

		select  6800
		type    6803

;******************************************************************
;* START
;******************************************************************

START           SEI                     ;disable ints
		LDS     STACK_TOP       ;#$00F7
		LDX     #$00FF          ;start at top of RAM
CLRRAM_LOOP     CLR     $0000,X         ;zero out the byte
		DEX                     ;next byte down
		CPX     #$0080          ;are we done?
		BCC     CLRRAM_LOOP     ;if carry not set then loop
		JSR     INIT            ;init ports and variables
		LDAA    T_CNTLSTAT      ;reset timer bits
		LDD     COUNTER_HI      ;get current counter
		ADDD    #TIMER_VALUE    ;add in delay value
		STD     OUT_COMPARE     ;store new counter destination
		CLI                     ;enable ints
		JMP     PROCESS_KB      ;go do process loop

;*********************************************************************
;* Test for valid key and apply modifiers, returns key or -1 if error
;*********************************************************************

ADJUST_KEY      ANDA    #$007F          ;strip key's hi bit
		CMPA    #$0048          ;is it < 48?
		BMI     APPLY_MOD       ;yes, modify key
		LDAA    #$00FF          ;bad key, return -1
		RTS

;***********************************************************************
;*  APPLY_MOD - A has key to modify
;*              X points to modifier byte 0-15
;*              returns modified key in A
;***********************************************************************

APPLY_MOD       LDAB    $0000,X         ;get modifiers
		ANDB    #$000F          ;constrain to <16
		LDX     #MODIFIER_TBL   ;MF834 pt to table
		ABX                     ;add mod to index tbl
		LDAB    $0000,X         ;get adjustment from table
		ABA                     ;add adjustment to key
		RTS                     ;return

;$00 is unshifted
;$48 is shifted
;$90 is control

MODIFIER_TBL    equ     $    ;MF834
		db      $00,$90,$48,$90,$00,$90,$48,$90
		db      $48,$90,$48,$90,$48,$90,$48,$90

;************************************************************************
;* SCAN_KB - returns EORed scan value in A, 0 if none
;*           normalized scan value in B
;*           X points to key enable table
;************************************************************************

SCAN_KB         LDAB    #NUM_ROWS                ;#$000D - init number of
		STAB    TEMP_VAR_0               ;$0082 - rows to scan
		ABX
SCAN_KB_LOOP    LDAA    TEMP_VAR_0               ;$0082 - get current scan row
		DECA                             ;decrement it
		JSR     SCAN_ONE_ROW             ;scan the row
		DEX                              ;dec the key enable ptr
		TAB                              ;copy inverse column data to B
		COMB                             ;normalize column
		EORA    $0000,X                  ;XOR A with enable table value
		BNE     SKB_KEYDOWN              ;skip if anything is left
		DEC     TEMP_VAR_0               ;$0082-else try next row
		BNE     SCAN_KB_LOOP             ;loop until done
SKB_KEYDOWN     TSTA                             ;get anything?
		BEQ     SKB_EXIT                 ;nope, skip
		DEC     TEMP_VAR_0               ;$0082, dec current row
SKB_EXIT        RTS

;*************************************************************************
;* PROCESS_ASCII
;* Takes keycode in A
;* Returns FF if bad key or ASCII value
;*************************************************************************

PROCESS_ASCII   STAA    KEY_CODE         ;$0088
		STAA    TEMP_VAR_3       ;save keycode
		ANDA    #$0080           ;isolate bit 7
		STAA    $008A            ;save that
		LDAA    TEMP_VAR_3       ;get keycode again
		ANDA    #$007F           ;isolate lower 7 bits
		STAA    $0089            ;save value
		EORA    #$0048           ;is it $48 - (Control key)
		BEQ     GOT_MOD_KEY      ;yes-skip
		LDAA    $0089            ;get lower 7 bits again
		EORA    #$0050           ;is it $50? Shift key
		BEQ     GOT_MOD_KEY      ;yes-skip
		LDAA    $0089            ;get lower 7 bits again
		EORA    #$0058           ;is it $58? Lock key
		BNE     NOT_MOD_KEY      ;no-skip
GOT_MOD_KEY     LDAA    KEY_CODE         ;$0088 - get key
		LDX     #MOD_BYTE        ;#$008B - point to mod byte
		JSR     TEST_MOD_KEYS
		LDAA    #$00FF           ;return $FF, don't process mod chars
		CLR     REPEAT_DISABLE
		BRA     JUST_RET7

NOT_MOD_KEY     LDAA    $008A
		CLR     REPEAT_DISABLE
		COMA
		BITA    #$0080
		BNE     PRO_ASCII_2
		COM     REPEAT_DISABLE
PRO_ASCII_2     LDAA    $0089           ;get lower 7 bits again
		EORA    #$0060          ;is it $60 (Home key)
		BNE     DO_ASCII        ;no, skip to asciiize
		LDAA    #HOME_KEY       ;#$0080
		BRA     JUST_RET7       ;return ASCII key val

DO_ASCII        LDX     #MOD_BYTE       ;#$008B - point to mod byte
		LDAA    KEY_CODE        ;$0088  - get key code value
		JSR     GET_ASCII       ;get an ASCII equivalent
JUST_RET7       RTS

;******************************************************************
;* READ_KB      X points to enable table
;*              Returns FF if no key is pressed
;******************************************************************

READ_KB         STX     ETBL_PTR                ;$008C save enable bits table addr
		JSR     SCAN_KB                 ;scan the keyboard
		STAA    TMP_SCAN_RES            ;$008E store EOR scan result
		PSHB                            ;save normalized column
		LDAB    TEMP_VAR_0              ;$0082 - get the scan row
		STAB    TMP_ROW                 ;$008F and store it
		PULB                            ;restore normalized column
		TSTA                            ;test EOR scan result
		BEQ     RKB_EXIT                ;skip if 0 - return -1
		TBA                             ;get normalized col scan in A
		JSR     CROSSED_ROWS            ;multi keys & crossed rows?
		TSTA                            ;test return value
		BNE     RKB_EXIT_0              ;multi keys, exit -1
		LDX     #SCAN_AND_0             ;$0090 push destination address
		PSHX
		LDX     ETBL_PTR                ;$008C - pt to key enable bits
		LDAA    TMP_SCAN_RES            ;$008E - get EOR col data
		STAA    TEMP_VAR_0              ;$0082 - save it
		LDAA    TMP_ROW                 ;$008F - get the row number
		JSR     LF8EB
		LDX     #SCAN_AND_0             ;$0090 get dest address
		JSR     LF9C7
		STAA    TEMP_VAR_0               ;$0082
		LDAA    TMP_ROW                  ;$008F
		JSR     MPLEX_0127               ;mix col 0 1 2 7 with row
		BRA     JUST_RET6                ;exit
RKB_EXIT_0      LDAA    #$00FF
JUST_RET6       BRA     JUST_RET0        ;LF8EA
RKB_EXIT        LDAA    #$00FF
JUST_RET0       RTS                      ;LF8EA

;***************************************************************
;* X points to key enable table base
;* A has row number where keypress was detected
;* Destination address for values has been pushed previous to call
;***************************************************************

LF8EB           TAB                     ;store row in B
		ABX                     ;index into enable table
		LDAA    $0000,X         ;get byte there into A
		TAB                     ;and B
		COMA                    ;invert enable bits
		ANDA    TEMP_VAR_0      ;$0082-and inverted with scan result
		PULX                    ;get return addres
		STX     TMP_RET_ADDR    ;$0092 - save it
		PULX                    ;get destination address
		STAA    $0000,X         ;store modified col scan at dest+0
		ANDB    TEMP_VAR_0      ;$0082 - and normal with scan result
		STAB    $0001,X         ;store it at dest+1
		LDX     TMP_RET_ADDR    ;$0092 - get return address
		PSHX                    ;push
		RTS                     ;return

;*************************************************************************
;* MPLEX_0127 - multiplexes the row number and 4 column lines
;* A has TMP_ROW
;* Returns a multiplex of rows and columns 0 1 2 and 7
;*************************************************************************

MPLEX_0127      ASLA                    ;* 2
		ASLA                    ;* 4
		ASLA                    ;* 8
		LDAB    TEMP_VAR_0      ;$0082 - get EOR scan col data in B
		STAA    TEMP_VAR_0      ;$0082 - save 8 x row there
		ANDB    #$0087          ;mix in columns 7 2 1 0
		ORAB    TEMP_VAR_0      ;$0082
		TBA                     ;and move to A
		RTS

;******************************************************************
;* GET_ASCII - Keycode in A
;*             Returns ASCII value in A, or -1 if bad key
;******************************************************************

GET_ASCII       JSR     ADJUST_KEY      ;range test and modify key
		INCA                    ;returned -1?
		BEQ     GET_ASCII_ERR   ;yes-error exit
		DECA                    ;restore the key #
		PSHX                    ;save X
		LDX     #CHAR_TBL       ;MFAF8 char tbl base
		TAB                     ;copy key to B
		ABX                     ;index into table
		LDAA    $0000,X         ;get the ASCII value
		PULX                    ;restore X
		BRA     JUST_RET1       ;skip for good return
GET_ASCII_ERR   LDAA    #$00FF
JUST_RET1       RTS                     ;LF922

;****************************************************************
;* STILL_PRESSED
;* Row/Col0127  multiplex in A, no bit 7
;* Returns 80H if key is still pressed
;* Returns 00H if key is not pressed
;****************************************************************

STILL_PRESSED   STAA    TEMP_VAR_1      ;save key code
		ANDA    #$0007          ;isolate col 0,1,2 of keypress
		STAA    TEMP_VAR_2      ;save col number
		LDAA    TEMP_VAR_1      ;get row/col multiplex
		ANDA    #$0078          ;isolate row number
		LSRA                    ;/2
		LSRA                    ;/4
		LSRA                    ;/8
		STAA    TEMP_VAR_1      ;save row number
		JSR     SCAN_ONE_ROW    ;scan the row, active col is 0
		STAA    TEMP_VAR_0      ;$0082 - save new column data
		LDAB    TEMP_VAR_2      ;get the col number to match
		PSHX                    ;save X
		LDX     #BIT_VALUES     ;point to bit value table
		ABX                     ;index into table
		LDAB    $0000,X         ;get the bit value there
		ANDB    TEMP_VAR_0      ;$0082 - is the column still pressed?
		PULX                    ;restore X
		BNE     STILLP_OK       ;no, scan must have returned FF
		LDAA    #$0080          ;yes return $80 as still down code
		BRA     STILLP_EXIT
STILLP_OK       CLRA                    ;return 0 if key is no longer down
STILLP_EXIT     RTS

;********************************************************
;* TEST_MOD_KEYS
;* A has ???
;* X has ???
;********************************************************

TEST_MOD_KEYS   STAA    TEMP_VAR_2
		CLR     $00A7           ;make $A7 = 0
		LDAB    #$00FF          ;save exit code
		STAB    TEMP_VAR_3
		ANDA    #$007F          ;isolate lower 7 bits
		STAA    TEMP_VAR_0    ;$0082 - save
		EORA    #$0048          ;is it Control?
		BEQ     DO_CNTRL_KEY    ;yes, skip
		LDAA    TEMP_VAR_0    ;$0082 - get value again
		EORA    #$0050          ;is it Shift?
		BEQ     DO_SHIFT_KEY    ;yes, skip
		LDAA    TEMP_VAR_0    ;$0082 - get value again
		EORA    #$0058          ;is it Lock?
		BEQ     DO_LOCK_KEY     ;yes, skip
		BRA     TEST_MOD_NOT    ;no, skip

DO_CNTRL_KEY    LDAA    TEMP_VAR_2
		COMA
		BITA    #$0080
		BNE     DO_CNTRLK_EXIT
		COM     $00A7           ;$A7 = $FF
DO_CNTRLK_EXIT  LDAA    #$0000          ;code for CNTRL
		BRA     TEST_MOD_EXIT

DO_SHIFT_KEY    LDAA    TEMP_VAR_2
		COMA
		BITA    #$0080
		BNE     DO_SHIFTK_EXIT
		COM     $00A7           ;$A7 = $FF
DO_SHIFTK_EXIT  LDAA    #$0001          ;code for SHIFT
		BRA     TEST_MOD_EXIT

DO_LOCK_KEY     LDAA    TEMP_VAR_2
		COMA
		BITA    #$0080
		BNE     DO_LOCKK_EXIT
		LDAA    #$0008
		EORA    $0000,X
		STAA    $0000,X
		COM     $00A7           ;$A7 = $FF
DO_LOCKK_EXIT   LDAA    #$0002          ;code for LOCK
		BRA     TEST_MOD_EXIT

TEST_MOD_NOT    INC     TEMP_VAR_3      ;not mod key, make exit code 0
TEST_MOD_EXIT   LDAB    TEMP_VAR_3      ;get exit code
		BEQ     JUST_RET2       ;if 0 (not mod) then just return
		LDAB    $0000,X
		STAB    $0081
		PSHX
		LDAB    $00A7           ;$A7 is $FF if bit 7 of ?? not set
		BNE     TEST_MOD_2      ;skip if so
		TAB                     ;else get mod key code
		LDX     #BIT_VALUES     ;look up bit mask for it
		ABX
		LDAB    $0000,X
		COMB                    ;invert it
		ANDB    $0081           ;reset that bit in ??
		STAB    $0081
		BRA     TEST_MOD_OUT

TEST_MOD_2      TAB                     ;get mod key code
		LDX     #BIT_VALUES     ;look up bit mask for it
		ABX
		LDAB    $0000,X
		ORAB    $0081           ;set that bit in ??
		STAB    $0081
TEST_MOD_OUT    PULX
		STAB    $0000,X         ;save new value
JUST_RET2       RTS                     ;LF9C6

;*************************************************************
;* X pts to modified scan data
;*************************************************************

LF9C7           CLR     TEMP_VAR_0            ;$0082
LF9CA           INX                             ;pt to data 2 (AND normal enable)
		LDAA    $0000,X                 ;get the mod scan data
		RORA                            ;get bit 0 into Carry
		STAA    $0000,X                 ;save the new value
		BCC     LF9EC                   ;skip if carry clear
		PSHX                            ;save data ptr
		LDAB    #$0006                  ;get value of bit 6
		LDX     #BIT_VALUES
		ABX
		LDAB    $0000,X                 ;in B
		ORAB    TEMP_VAR_0            ;$0082 - mix with ??
		STAB    TEMP_VAR_0            ;$0082 - and save it back
		LDAB    #$0007                  ;get value of bit 7
		LDX     #BIT_VALUES
		ABX
		LDAB    $0000,X                 ;into B
		ORAB    TEMP_VAR_0            ;$0082 - mix with ??
		STAB    TEMP_VAR_0            ;$0082 - and save it back
		PULX                            ;restore data ptr
LF9EC           DEX                             ;ptr to data 1
		LDAA    $0000,X                 ;get data1
		RORA                            ;get bit 0 into carry
		STAA    $0000,X                 ;store new value
		BCC     LFA02                   ;skip if carry clear
		PSHX                            ;save data ptr
		LDAB    #$0006                  ;get value for bit 6
		LDX     #BIT_VALUES
		ABX
		LDAB    $0000,X                 ;into B
		ORAB    TEMP_VAR_0            ;$0082 - mix with ??
		STAB    TEMP_VAR_0            ;$0082 - save it back
		PULX                            ;restore data ptr
LFA02           LDAA    TEMP_VAR_0            ;$0082 - get ??
		BITA    #$0040                  ;is bit 6 set?
		BNE     LFA0B                   ;yes, skip
		INC     TEMP_VAR_0            ;$0082 - no, inc ??
LFA0B           LDAA    TEMP_VAR_0            ;$0082 - get ??
		BITA    #$0040                  ;is bit 6 set?
		BNE     JUST_RET3               ;LFA17 - yes, skip
		BITA    #$0008                  ;is bit 3 set?
		BNE     JUST_RET3               ;LFA17 - yes, skip
		BRA     LF9CA                   ;LF9CA - no, loop back

JUST_RET3       RTS                    ;LFA17

;*************************************************************
;* CROSSED_ROWS
;* Normalized column in A
;* Returns 0 if OK
;* Returns FFH if multiple keys pressed and rows were crossed
;*
;* Apparantly we can deal with multiple keypresses, as long as they
;* are in the row we scanned, otherwise error out ?
;*************************************************************

CROSSED_ROWS    JSR     GET_BIT_CNT     ;how many columns are set?
		DECA
		BEQ     JUST_RET4       ;just one?, then skip out OK

		;if another key on our current active column is pressed,
		;then the connection will drive the second keys row low,
		;therefore we can't really know if the column bits we
		;are looking at came from the row we scanned, or the
		;second key's row.  So, if we detected multiple column
		;bits on, then here we determine whether they came
		;from the row we scanned or not.
		;Output row should read low eh?

		LDAA    P3_DATA         ;read the low rows
		COMA                    ;normalize the row
		JSR     GET_BIT_CNT     ;how many rows are set?
		STAA    TEMP_VAR_0    ;$0082 - save count for a sec
		LDAA    P4_DATA         ;read hi rows
		COMA                    ;normalize
		ANDA    #$001F          ;masks out unused bits
		JSR     GET_BIT_CNT     ;count bits
		ADDA    TEMP_VAR_0    ;$0082 add to running total
		DECA                    ;just 1 row set?
		BEQ     JUST_RET4       ;yes, skip out OK
		LDAA    #$00FF          ;no, flag multiple keys error
JUST_RET4       RTS                     ;LFA35

;*******************************************************************
;* GET_BIT_CNT - get column count
;* input: Normalized column scan in A
;* out:   # of bits set in input
;*******************************************************************

GET_BIT_CNT     LDAB    #$0000
		STAB    NUM_BITS
		LDAB    #$0008
		STAB    TMP_BIT_CNTR
GET_BIT_LOOP    RORA                    ;rotate bit 0 into carry
		BCC     GET_BIT_SKIP    ;if clear, skip
		INC     NUM_BITS
GET_BIT_SKIP    DEC     TMP_BIT_CNTR    ;dec bit count
		BNE     GET_BIT_LOOP    ;loop until done
		LDAA    NUM_BITS    ;return set count
		RTS

;***************************************************************
;*  A has row/column multiplex
;*  X has enable table ptr
;***************************************************************

SET_ENABLE_BITS STAA    TEMP_VAR_3
		ANDA    #$0080          ;isolate bit 7
		STAA    TEMP_VAR_1      ;save it
		LDAA    TEMP_VAR_3      ;get value again
		ANDA    #$0078          ;mask out bits 7 2 1 0 (column)
		LSRA                    ;/2
		LSRA                    ;/4
		LSRA                    ;/8 row number normalized
		STAA    TEMP_VAR_2      ;save it
		LDAA    TEMP_VAR_3      ;get value again
		ANDA    #$0007          ;isolate column data
		STAA    TEMP_VAR_3      ;save it
		LDAA    TEMP_VAR_1      ;get bit 7 info
		BEQ     SET_ENABLE_SET  ;skip if not set
		LDAB    TEMP_VAR_2      ;get row number in B
		ABX
		LDAA    $0000,X         ;look up table val
		STAA    $0081           ;save it
		LDAB    TEMP_VAR_3      ;get column data
		PSHX
		LDX     #BIT_VALUES
		ABX
		LDAB    $0000,X         ;look up matching bit value
		COMB                    ;invert it
		ANDB    $0081           ;reset that bit in table
		STAB    $0081
		PULX
		STAB    $0000,X         ;save new value
		BRA     JUST_RET5

		;bit 7 not set
SET_ENABLE_SET  LDAB    TEMP_VAR_2      ;get row number in B
		ABX
		LDAA    $0000,X         ;look up row val in table
		STAA    $0081           ;save it
		LDAB    TEMP_VAR_3      ;get column data
		PSHX
		LDX     #BIT_VALUES
		ABX
		LDAB    $0000,X         ;look up matching bit value
		ORAB    $0081           ;set that bit in table
		STAB    $0081
		PULX
		STAB    $0000,X         ;save new value
JUST_RET5       RTS                     ;LFA96

;*********************************************************************
;* SEQUENCER - branches to the appropriate routine for this sequence
;*             Returns and ASCII value or $FF if no key was pressed
;*********************************************************************

SEQUENCER       LDAA    #$00FF          ;store "no key" code as return value
		STAA    SEQ_ASCII
		LDAA    #$0000
		STAA    $0095
		LDAA    SEQUENCE_NUM    ;$0096 get sequence number
		CMPA    #$0000          ;is it 0?
		BEQ     SEQUENCE_1      ;yes, skip
		DECA                    ;is it 1
		BEQ     SEQUENCE_2      ;yes, skip
		BRA     SEQUENCE_3      ;must be 2, skip

;*****************************************************************
;* SEQUENCE_1 - read initial char from keyboard
;*****************************************************************

SEQUENCE_1      LDX     #ENABLE_TBL              ;$0099 point X at table
		JSR     READ_KB                  ;read the keybaord
		CMPA    #$00FF                   ;get anything?
		BEQ     SEQUENCE_1_X             ;no-skip
		STAA    SEQUENCE_SCAN            ;yes, save it
		JSR     GET_LOW_CNTUP            ;get time counter
		ADDA    #$000A                   ;add 10 milliseconds
		STAA    DEBOUNCE_TIME            ;and save
		LDAA    #$0001                   ;set next sequence
		STAA    SEQUENCE_NUM             ;$0096
SEQUENCE_1_X    BRA     SEQUENCER_EXIT

:**************************************************************
;* SEQUENCE_2 - wait for debounce time
;**************************************************************

SEQUENCE_2      JSR     GET_LOW_CNTUP           ;get current time cntr
		LDAB    DEBOUNCE_TIME           ;and our debounce time
		CBA                             ;are they the same?
		BNE     SEQUENCE_2_X            ;no, skip out
		LDAA    #$0002                  ;yes, set next sequence
		STAA    SEQUENCE_NUM            ;$0096
SEQUENCE_2_X    BRA     SEQUENCER_EXIT

;*************************************************************
;* SEQUENCE_3 - debounce the key
;*************************************************************

SEQUENCE_3      LDAA    SEQUENCE_SCAN           ;get key seq1 read
		ANDA    #$007F                  ;strip bit 7
		JSR     STILL_PRESSED           ;is it still down?
		STAA    TEMP_VAR_1              ;save return value 0 or $80
		LDAA    SEQUENCE_SCAN           ;get seq1 key again
		ANDA    #$0080                  ;mask out lower bits
		EORA    TEMP_VAR_1              ;if one or the other's hi bit set
		BNE     SEQUENCE_3_X            ;then, skip out, bad debounce
		LDAA    SEQUENCE_SCAN           ;get key again
		LDX     #ENABLE_TBL             ;$0099
		JSR     SET_ENABLE_BITS
		LDAA    SEQUENCE_SCAN           ;get key again
		JSR     PROCESS_ASCII           ;get an ascii value
		STAA    SEQ_ASCII               ;store it to return
SEQUENCE_3_X    LDAA    #$0000                  ;set for sequence 0
		STAA    SEQUENCE_NUM            ;$0096
SEQUENCER_EXIT  LDAA    SEQ_ASCII
		RTS

;*************************************************************
;* This table is organized as 9 rows of 8 columns each
;* The keys appear on the keyboard lines in this order.
;* The table is extended to accomodate shifted and control
;* key value lookups.
;*************************************************************

CHAR_TBL        equ     $         ;MFAF8
		db      $90,$81,$82,$83,$84,$85,$86,$91
		db      "12345678"
		db      $09
		db      "qwertyuasdfghjkzxcvbnm,90-+^l;'iop[]./"
		db      $0D,$5C,$1B
		dw      $FFFF
		db      $20,$92,$93,$96,$A0,$A1,$A2,$A3
		db      $08,$94,$95,$97,$98,$89,$8A,$8B
		db      $8C,$8D,$8E,$99
		db      "!@#$%_&*"
		db      $B9
		db      "QWERTYUASDFGHJKZXCVBNM<()`=~L:"
		db      $22
		db      "IOP{}>?"
		db      $0D,$7C,$1B
		dw      $FFFF
		db      $20,$9A,$9B,$9E,$A0,$A1,$A2,$A3
		db      $08,$9C,$9D,$9F,$90,$81,$82,$83
		db      $84,$85,$86,$91,$31,$00,$33,$34
		db      $35,$1F,$37,$38,$09,$11,$17,$05
		db      $12,$14,$19,$15,$01,$13,$04,$06
		db      $07,$08,$0A,$0B,$1A,$18,$03,$16
		db      $02,$0E,$0D
		db      ",90-+"
		db      $1E,$0C,$3B,$27,$09,$0F,$10,$1B
		db      $1D,$2E,$2F,$0D,$1C,$1B
		dw      $FFFF
		db      $20,$92,$93,$96,$A4,$A5,$A6,$A7
		db      $08,$94,$95,$7F

BIT_VALUES      equ     $
		db      $01,$02,$04,$08,$10,$20,$40,$80

;*********************************************************************
;* SCAN_ONE_ROW - A has row number to scan, use lookup table to find
;*                which I/O line to drive low, then read input at P1
;*                Returns scan data in A, all bits 1 except active
;*                keypress column.
;*********************************************************************

SCAN_ONE_ROW    PSHX                            ;save X
		TAB                             ;save row count
		ASLB                            ;* 2 for word offset
		LDX     #OUTPUT_DIR_TBL         ;MFBEC - get output tbl base
		ABX                             ;add in offset
		LDD     $0000,X                 ;get the word there
		STD     P3_DIR                  ;store in P3 and P4 direction
		LDD     #$0000                  ;output lows on selected line
		STD     P3_DATA                 ;on both P3 and P4
		LDAA    P1_DATA                 ;read data on P1
		PULX                            ;restore X
		RTS

OUTPUT_DIR_TBL  equ     $                       ;MFBEC
		dw      $0100,$0200,$0400,$0800
		dw      $1000,$2000,$4000,$8000
		dw      $0001,$0002,$0004,$0008
		dw      $0010

;*******************************************************************
;* GET_A_KEY Called by main PROCESS_KB routine
;* Returns an ASCII value in A or $FF if no key was pressed
;*******************************************************************

GET_A_KEY       JSR     SEQUENCER       ;do a keyboard sequence
		STAA    TEMP_VAR_2      ;save the ACSII value
		CLRA                    ;A=0
		LDAB    REPEAT_DISABLE
		CMPB    #$00FF
		BNE     GET_A_KEY_0
		INCA                    ;A=A+1
GET_A_KEY_0     STAA    $00A6
		LDAA    TEMP_VAR_2      ;get the ASCII value
		JSR     GET_REPEAT_CHAR ;get or set up repeating char
		STAA    TEMP_VAR_2      ;save returned char
		LDAA    $00A6
		BNE     GET_A_KEY_X
		LDAA    #$00FF          ;store "no keypress" code
		STAA    TEMP_VAR_2
GET_A_KEY_X     LDAA    TEMP_VAR_2      ;return ASCII value
		RTS

;*****************************************************************
;* TIMER_INT  - this interrupt occurs approx every 1 millisecond
;*****************************************************************

TIMER_INT       LDAA    T_CNTLSTAT      ;reset the timer flags
		LDD     COUNTER_HI      ;get the current counter value
		ADDD    #TIMER_VALUE    ;add a timeout value to it
		STD     OUT_COMPARE     ;save the new destination time
		LDD     RST_CNTUP       ;$00B2 - get # of timeouts recorded
		ADDD    #$0001          ;add one
		STD     RST_CNTUP       ;save it
		DEC     RST_CNTDOWN     ;$00AB dec restart count
		BNE     TIMER_EXIT      ;skip if not 0 yet
		JMP     START           ;otherwise we are hung so restart
TIMER_EXIT      RTI

;********************************************************************
;* GET_LOW_CNTUP - returns low rst countup in A and B
;********************************************************************

GET_LOW_CNTUP   LDD     RST_CNTUP       ;$00B2
		STAA    TMP_BIT_CNTR
		TBA
		RTS

;*******************************************************************
;* PUT_BUFFER - puts the byte in A into the keyboard buffer
;*******************************************************************

PUT_BUFFER      PSHA                            ;save the byte
		LDD     BUF_EMPTY_INDEX         ;get empty and fill indexes
		CBA                             ;are they the same?
		BNE     PUT_BUF_0               ;no, go do it
		LDAA    BUF_HAS_DATA            ;yes, so test whether buffer
		CMPA    #$0000                  ;has data in it...
		BEQ     PUT_BUF_0               ;no, so go fill it
		PULA                            ;yes, so buffer is full
		BRA     PUT_BUF_EXIT            ;dump data

PUT_BUF_0       PULA
		LDX     #BUFFER_BASE              ;#$00B4 - get buffer base
		ABX                               ;add buffer count to it
		STAA    $0000,X                   ;store value there
		INCB                              ;inc buffer count
		CMPB    #BUFFER_MAX               ;#$0014 - is buffer full?
		BNE     NOT_OVERRUN               ;no-skip
		CLRB                              ;yes-make count 0
NOT_OVERRUN     STAB    BUF_FILL_INDEX            ;$00C9 store new buf count
PUT_BUF_EXIT    LDAA    #$00FF                    ;set buffer has data flag
		STAA    BUF_HAS_DATA
		RTS

;*****************************************************************
;* GET_BUFFER - returns then next character in the KB buffer or
;*              FF if none.
;*****************************************************************

GET_BUFFER      PSHB
		LDAA    BUF_HAS_DATA    ;is there anything in the buffer?
		CMPA    #$0000
		BNE     GET_BUF_0       ;yes-go get it
		LDAA    #$00FF          ;no-return 0FFH
		BRA     GET_BUF_EXIT

GET_BUF_0       LDX     #BUFFER_BASE    ;#$00B4 get buffer base
		LDAB    BUF_EMPTY_INDEX ;and empty index
		ABX                     ;add in index
		LDAA    $0000,X         ;get the value there
		INCB                    ;inc the index
		CMPB    #BUFFER_MAX     ;#$0014 - need to wrap?
		BNE     GET_BUF_1       ;no-skip
		CLRB                    ;yes-make index 0
GET_BUF_1       STAB    BUF_EMPTY_INDEX ;save new empty index
		CMPB    BUF_FILL_INDEX  ;$00C9 - empty index == fill index?
		BNE     GET_BUF_EXIT    ;no-skip
		LDAB    #$0000          ;yes, reset buffer has data flag
		STAB    BUF_HAS_DATA
GET_BUF_EXIT    PULB
		RTS

;********************************************************************
;* INIT - using the values in INIT_TBL, initialize the 6801 ports and
;*        program variables
;********************************************************************

INIT            LDX     #INIT_TBL       ;get table base
INIT_LOOP       LDAA    $0000,X         ;get byte from table
		PSHX                    ;save table index
		LDX     $0001,X         ;read address from table into X
		STAA    $0000,X         ;store byte there
		PULX                    ;restore table index
		INX                     ;inc to next byte,address pair
		INX
		INX
		CPX     #LAST_TBL1_ADDR ;are we done?
		BNE     INIT_LOOP       ;no, loop
		RTS

;**********************************************************************
;* INIT_TBL - table of init values for various memory addresses.
;*            Format is 1 byte data, 1 word address
;**********************************************************************

INIT_TBL        equ     $
		db      $00
		dw      SEQUENCE_NUM     ;$0096
		db      $FF                     ;init enable tbl to FF
		db      ENABLE_TBL              ;$0099    (13 bytes)
		db      $FF
		dw      $009A
		db      $FF
		dw      $009B
		db      $FF
		dw      $009C
		db      $FF
		dw      $009D
		db      $FF
		dw      $009E
		db      $FF
		dw      $009F
		db      $FF
		dw      $00A0
		db      $FF
		dw      $00A1
		db      $FF
		dw      $00A2
		db      $FF
		dw      $00A3
		db      $FF
		dw      $00A4
		db      $FF
		dw      $00A5
		db      $00             ;port 1 all inputs
		dw      P1_DIR
		db      $10             ;port 2 bit 4 is out TX, rest in
		dw      P2_DIR
		db      $FF             ;port 3 all outputs
		dw      P3_DIR
		db      $FF             ;port 4 all outputs
		dw      P4_DIR
		db      $04             ;62.5 baud, internal clock
		dw      SCI_RM
		db      $1A             ;enable rx, tx, rx int
		dw      SCI_TR_CS
		db      $08             ;enable output compare int
		dw      T_CNTLSTAT
LAST_TBL1_ADDR  EQU     $

;********************************************************************
;* PROCESS_KB
;********************************************************************

PROCESS_KB      LDAA    #$0010          ;load restart countdown value
		STAA    RST_CNTDOWN     ;$00AB
		LDAA    NEW_KB_BYTE     ;$00AA - get new kb byte if any
		CLR     NEW_KB_BYTE     ;$00AA - clear it
		TSTA                    ;is new one 0?
		BNE     GOT_NEW_ONE     ;nope, go set up for sending
		JSR     GET_A_KEY       ;yes, so scan keyboard
		JSR     IS_ARROW        ;is it an arrow key?
		BCC     DO_ARROW        ;yes, skip
		CMPA    #$00FF          ;no, is it a valid char?
		BEQ     PKB_NOTHING_NEW ;no, skip

					;else

GOT_NEW_ONE     TAB                     ;store new data in B
		LDAA    DATA_AVAIL      ;is there data waiting to be sent?
		BNE     DATA_READY      ;yes-skip
		JSR     GET_BUFFER      ;no, get a buffered byte
		CMPA    #$00FF          ;none?
		BEQ     NO_BUFDATA      ;then skip
		STAA    CUR_DATA        ;got one, store it to be sent
		TBA                     ;restore new data
		JSR     PUT_BUFFER      ;buffer it
		BRA     BUFFERED_IT

NO_BUFDATA      STAB    CUR_DATA        ;if no buf data, just store new data
BUFFERED_IT     LDAA    #$0001          ;data available = TRUE
		STAA    DATA_AVAIL
		BRA     PRO_KB_0        ;go process kb again

DATA_READY      TBA                     ;if data is already waiting
		JSR     PUT_BUFFER      ;just buffer the new data
PRO_KB_0        BRA     PRO_KB_END      ;go process kb again

PKB_NOTHING_NEW LDAA    DATA_AVAIL      ;is there data waiting to be sent?
		BNE     PRO_KB_END      ;yes-exit
		JSR     GET_BUFFER      ;get a byte from the buffer
		CMPA    #$00FF          ;is buffer empty?
		BEQ     PRO_KB_END      ;yes-exit
		STAA    CUR_DATA        ;store data to be sent
		LDAA    #$0001          ;data available = TRUE
		STAA    DATA_AVAIL
PRO_KB_END      BRA     PROCESS_KB

;********************************************************************
;* DO_ARROW - here if A contains ASCII arrow key or home.
;*            2 of these keys may be pressed within .5 second to
;*            generate a diffenent ASCII value
;********************************************************************

DO_ARROW        STAA    TMP_KB_BYTE     ;save the arrow key
		LDX     #$003C          ;load timeout counter
DO_ARROW_LOOP   LDAA    #$0010          ;install new restart countdown
		STAA    RST_CNTDOWN     ;$00AB
		PSHX                    ;save counter
		JSR     GET_A_KEY       ;get one
		PULX                    ;restore counter
		CMPA    #$00FF          ;did we get another key?
		BNE     DO_ARROW_2      ;yes, skip
		DEX                     ;no, dec counter
		BNE     DO_ARROW_LOOP   ;if not timed out, loop
		BRA     DOA_SINGLE      ;timed out, go process single key

DO_ARROW_2      STAA    NEW_KB_BYTE     ;$00AA - save second key
		LDAB    TMP_KB_BYTE     ;get first key
		SBA                     ;A-B
		BEQ     DOA_SINGLE      ;if they're the same key, skip
		LDAA    NEW_KB_BYTE     ;$00AA - get second key again
		BSR     IS_ARROW        ;is it also an arrow key?
		BCS     DOA_SINGLE      ;no, skip out
		CMPB    #HOME_KEY       ;#$0080 - is 1st key HOME?
		BEQ     HOME_1ST        ;yes, skip
		CMPA    #HOME_KEY       ;#$0080 - is 2nd key HOME?
		BNE     TWO_ARROWS      ;no, go check for 2 arrows
		BRA     HOME_2ND

HOME_1ST        TAB                      ;get arrow key in B
HOME_2ND        ANDB    #$0003           ;isolate lower 2 bits for lookup
		LDX     #HOME_PLUS_KEYS  ;get base of HOME+ ascii values
GET_ARROW_VAL           ABX                      ;index into table
		LDAA    $0000,X          ;get value there
		CLR     TMP_KB_BYTE      ;clear arrow 1 temp
		CLR     NEW_KB_BYTE      ;$00AA - and arrow 2 temp
DOA_DONE        BRA     GOT_NEW_ONE      ;go process new key

DOA_SINGLE      LDAA    TMP_KB_BYTE     ;get first arrow key
		BRA     DOA_DONE        ;go process it

TWO_ARROWS      EORA    TMP_KB_BYTE     ;get differences between 1 and 2
		ANDA    #$0001          ;bit zero will be set for legal combinations
		BEQ     DOA_SINGLE      ;not legal, go process just one
		ANDB    #$0003          ;isolate key1 arrow number
		LDAA    NEW_KB_BYTE     ;$00AA get arrow 2 temp
		ANDA    #$0003          ;isolate key2 arrow number
		ABA                     ;add them
		TAB                     ;save result in B
		CMPA    #$0003          ;is it UP-LEFT combo?
		BNE     NOT_UPLEFT      ;no-skip
		LDAA    TMP_KB_BYTE     ;yes, get key 1
		CMPA    #$00A3          ;is it the left arrow?
		BEQ     INC_ARROW_TOT   ;yes, skip to bump total
		LDAA    NEW_KB_BYTE     ;$00AA no, get key 2
		CMPA    #$00A3          ;is it the left arrow?
		BNE     NOT_UPLEFT      ;no-skip
INC_ARROW_TOT   INCB
NOT_UPLEFT      LDX     #MULTIARROW_TBL ;table to use in X
		BRA     GET_ARROW_VAL   ;go get value and process

MULTIARROW_TBL  equ     $
		db      $FF,$A8,$FF,$A9,$AB,$AA

;*******************************************************************
;* IS_ARROW - returns with carry clear if char in A is arrow or home key
;*******************************************************************

IS_ARROW        LDX     #ARROW_KEYS             ;pt to arrow key table
IS_ARROW_LOOP   CMPA    $0000,X                 ;is A == to table value?
		BEQ     IS_ARROW_OK             ;yes - skip
		INX                             ;inc index
		CPX     #HOME_PLUS_KEYS         ;are we done yet?
		BNE     IS_ARROW_LOOP           ;no, loop for mare
		SEC                             ;yes, not found, set carry
		RTS

IS_ARROW_OK     CLC                             ;found it, clear carry
		RTS

		;HOME, UP, RIGHT, DOWN, LEFT
ARROW_KEYS      db      $80,$A0,$A1,$A2,$A3

		;HOME+UP, HOME+RIGHT, HOME+DOWN, HOME+LEFT
HOME_PLUS_KEYS  db      $AC,$AD,$AE,$AF

;**************************************************************
;* GET_REPEAT_CHAR
;* A has ASCII value the sequencer delivered to us or $FF if
;* no key ready.
;* Returns the ASCII value in A
;**************************************************************

GET_REPEAT_CHAR
		STAA    TEMP_VAR_1      ;save the ASCII value
		LDAA    REPEAT_STATE    ;get the repeat state
		DECA                    ;is it 1?
		BNE     NOT_REP_STATE_1 ;no-skip
		JSR     GET_LOW_CNTUP   ;state 1...
		LDAA    TMP_BIT_CNTR    ;recreate countup word
		PSHA                    ;save
		PSHB
		SUBD    REP_STATE1_TIME ;sub the state 1 dest time
		BLS     STATE1_NOT_TIME ;skip if countup <= state1 time
		LDAA    #$0002          ;set state 2
		STAA    REPEAT_STATE
		PULB                    ;restore countup word
		PULA
		ADDD    #$002D          ;add 45 (milliseconds?) to it
		STD     REP_STATE2_TIME ;save as state 2 timeout
		BRA     NOT_REP_STATE_1

STATE1_NOT_TIME PULB                    ;restore countup word
		PULA
NOT_REP_STATE_1 LDAB    REPEAT_DISABLE          ;are we supposed to repeat?
		CMPB    #$00FF
		BNE     NOT_REPEATING_CHAR      ;no, skip out, reset state
		LDAA    TEMP_VAR_1              ;get ASCII key value
		INCA                    ;is it -1?
		BEQ     SET_REP_NOKEY   ;yes, skip
		DECA                    ;restore value
		CMPA    #$0008          ;is it BS key?
		BEQ     REPEATING_CHAR  ;yes, skip
		CMPA    #$00B8          ;is it Shift BS?
		BEQ     REPEATING_CHAR  ;yes, skip
		CMPA    #$009F          ;is it < arrow keys?
		BLS     CHK_LOW_KEYS    ;yes, skip
		CMPA    #$00AC          ;is it <= Home+Up?
		BLS     REPEATING_CHAR  ;yes, skip
CHK_LOW_KEYS    CMPA    #$001F          ;is it < Space (normal cntrl key)
		BLS     NOT_REPEATING_CHAR  ;yes, skip
		CMPA    #$007F          ;is it > DEL (> than ASCII codes)
		BHI     NOT_REPEATING_CHAR  ;yes, skip
		BRA     REPEATING_CHAR  ;no, normal key...skip

		;here if key is $FF
SET_REP_NOKEY   LDAA    REPEAT_STATE    ;get repeat state
		DECA                    ;is it 2?
		DECA
		BNE     NOT_REP_STATE_2 ;no-skip
		JSR     GET_LOW_CNTUP   ;state 2: get countup word
		LDAA    TMP_BIT_CNTR
		PSHA
		PSHB
		SUBD    REP_STATE2_TIME ;sub value at $B0
		BLS     STATE2_NOT_TIME ;skip if countup <= $B0
		PULB                    ;restore countup
		PULA
		ADDD    #$002D          ;add 45 (milliseconds?) to it
		STD     REP_STATE2_TIME ;save it again
		LDAA    REPEAT_CHAR     ;load the current repeat char
		BRA     SET_REP_RTS     ;return it

STATE2_NOT_TIME PULB                    ;restore countup
		PULA
NOT_REP_STATE_2 BRA     SET_REP_EXIT    ;returns $FF key

		;its a repeating key
REPEATING_CHAR  JSR     GET_LOW_CNTUP   ;get timer count up word
		LDAA    TMP_BIT_CNTR
		ADDD    #$0215          ;add 533 (milliseconds?) to it
		BCS     SET_REP_EXIT    ;if overflow, skip
		STD     REP_STATE1_TIME ;save count up
		LDAA    #$0001          ;set repeat state 1
		STAA    REPEAT_STATE
		LDAA    TEMP_VAR_1      ;get ASCII data
		STAA    REPEAT_CHAR     ;save it as repeating char
		BRA     SET_REP_RTS     ;exit

		;it's a non-repeating key
NOT_REPEATING_CHAR
		LDAA    #$0000          ;set repeat state 0
		STAA    REPEAT_STATE
SET_REP_EXIT    LDAA    TEMP_VAR_1      ;get ascii data
SET_REP_RTS     RTS                     ;return it

;**********************************************************************
;* MAC - medium access controller
;*       network serial interrupt
;**********************************************************************

MAC             LDAA    XMITTING
		BEQ     GET_NET_IN
		DECA
		BEQ     LFE6C
		LDAA    #$0000
		STAA    XMITTING
		RTI

;*********************************************************************
;* GET_NET_IN
;*********************************************************************

GET_NET_IN      SEC                     ;set carry flag
		LDAB    SCI_TR_CS       ;read SCI status into B
		LDAA    SCI_RX          ;read SCI incoming data
		ANDB    #ORFE           ;overrun or framing error?
		BEQ     GET_NET_0       ;no-skip
		LDAA    #CNTRL          ;yes, reset state
		STAA    KB_STATE        ;save it
		BRA     GOTO_SLEEP      ;ignore rest of transmission

GET_NET_0       LDAB    KB_STATE        ;get the state in B
		BNE     CARRY_TST       ;skip if not CNTRL
		TAB                     ;save data in B
		ANDA    #$000F          ;isolate network address
		CMPA    #KB_NODE ;#$0001 - is it us?
		BNE     NOT_FOR_US      ;no-ignore rest of transmission
		TBA                     ;restore data to A
		SEC                     ;set carry
		BRA     CARRY_TST       ;continue
NOT_FOR_US      CLC                     ;clear carry, not for us
CARRY_TST       BCS     PROCESS_STATE   ;continue if carry set
GOTO_SLEEP      LDAB    #$001B          ;set wakeup bit
		STAB    SCI_TR_CS       ;save it
		RTI                     ;return from int

LFE6C           LDAA    SCI_TR_CS
PROCESS_STATE   LDAB    KB_STATE        ;FE6E get state in B
		ASLB                    ;* 2 for word offset
		LDX     #STATE_TBL      ;get state table base
		ABX                     ;add state offset to base
		LDX     $0000,X         ;get the routine address
		JMP     $0000,X         ;jump to it

STATE_TBL       equ     $
		dw      CONTROL         ;$00
		dw      LFEF5           ;$01    RTI
		dw      LFEF6           ;$02    RTI
		dw      LFEF7           ;$03    RTI
		dw      LFEF8           ;$04    RTI
		dw      DO_STAT_OUT     ;$05
		dw      DO_S_CS_OUT     ;$06
		dw      DO_S_ACK_IN     ;$07
		dw      DO_LENHI_OUT    ;$08
		dw      DO_LENLO_OUT    ;$09
		dw      DO_DATA_OUT     ;$0A
		dw      DO_CS_OUT       ;$0B
		dw      DO_ACK_IN       ;$0C

;******************************************************************
;* Control - State 0, Net data is in A
;******************************************************************

CONTROL         CMPA    #MN_RESET+KB_NODE       ;#$0001 - reset
		BNE     NOT_RESET
		LDX     RST_INT_ADDR            ;get restart address
		JMP     $0000,X                 ;jump there
NOT_RESET       CMPA    #MN_READY+KB_NODE       ;#$00D1 - ready
		BEQ     DO_READY
		CMPA    #MN_SEND+KB_NODE        ;#$0061 - send
		BEQ     DO_SEND
		CMPA    #MN_STATUS+KB_NODE      ;#$0011 - status
		BEQ     DO_STATUS
		CMPA    #MN_RECEIVE+KB_NODE     ;#$0041 - receive
		BNE     NOT_RCV
		JMP     DO_RCV                  ;LFF34
NOT_RCV         CMPA    #MN_CLR                 ;#$0031 - clr
		BNE     NOT_CLR
		JMP     DO_CLR                  ;LFF47
NOT_CLR         LDAA    #NM_NACK+KB_NODE        ;#$00C1 - Nack
		JSR     NET_XMIT                ;NACK the unrecognized cmd
		BCS     CNTRL_EXIT              ;and exit int if error
		JSR     XMIT_CLEANUP            ;or clean up if OK
CNTRL_EXIT      RTI                             ;return from int

;*********************************************************************
;* DO_READY - just NACKs
;*********************************************************************

DO_READY        LDAA    #NM_NACK+KB_NODE        ;#$00C1 - NACK
		JSR     NET_XMIT                ;send it
		BCS     DO_RDY_EXIT             ;skip if xmit error
		JSR     XMIT_CLEANUP            ;else clean up
DO_RDY_EXIT     RTI

;*********************************************************************
;* DO_SEND
;*********************************************************************

DO_SEND         LDAA    #NM_NACK+KB_NODE        ;#$00C1 - NACK
		JSR     NET_XMIT                ;send it
		BCS     DO_SEND_EXIT            ;skip if xmit error
		JSR     XMIT_CLEANUP            ;else clean up
DO_SEND_EXIT    RTI

;*********************************************************************
;* DO_STATUS - sends first byte of status msg
;*             Sets up SCI to interrupt when xmit complete
;*             Then will will get the next state DO_STAT_OUT
;*********************************************************************

DO_STATUS       LDAA    #$000F                  ;enable TX int, disable RX
		STAA    SCI_TR_CS
		LDX     #$0004                  ;# bytes to xmit
		STX     NUM_X_BYTES             ;save
		LDAA    #STATUS_OUT             ;#$0005 - set status state
		STAA    KB_STATE
		LDAA    #$0001                  ;xmitting = TRUE
		STAA    XMITTING
		LDAA    #$0001                  ;init status tbl offset
		STAA    STAT_OFFSET             ;to 1
		LDAA    ROM_STATUS_MSG          ;get byte from status tbl
		STAA    SCI_TX                  ;send it
		CLR     CHECKSUM                ;clear checksum
		RTI

LFEF5           RTI
LFEF6           RTI
LFEF7           RTI
LFEF8           RTI

;*******************************************************************
;* DO_STAT_OUT - we stay in this state until all status bytes have
;*             been sent
;*******************************************************************

DO_STAT_OUT     LDX     #ROM_STATUS_MSG         ;get base status table
		LDAB    STAT_OFFSET             ;get current offset
		ABX                             ;add
		LDAA    $0000,X                 ;load the byte there
		STAA    SCI_TX                  ;and send it
		EORA    CHECKSUM                ;mix in old checksum
		STAA    CHECKSUM                ;save new checksum
		INC     STAT_OFFSET             ;inc the status offset
		LDX     NUM_X_BYTES             ;get bytes to send
		DEX                             ;one less
		STX     NUM_X_BYTES             ;save
		BNE     STAT1_EXIT              ;skip if not done yet
		LDAA    #STAT_CS_OUT            ;#$0006 else, set up next state
		STAA    KB_STATE
STAT1_EXIT      RTI

;*********************************************************************
;* DO_S_CS_OUT
;*********************************************************************

DO_S_CS_OUT     LDAA    CHECKSUM                ;get the checksum
		STAA    SCI_TX                  ;send it
		LDAA    SCI_TR_CS               ;clear any pending ints
		LDAA    SCI_RX
		LDAA    #$001B                  ;enable RX and wakeup bit
		STAA    SCI_TR_CS               ;disable TX int, go to sleep
		LDAA    #$0000                  ;xmitting = FALSE
		STAA    XMITTING
		LDAA    #S_ACK_IN               ;#$0007-set next state
		STAA    KB_STATE
		RTI

;*********************************************************************
;* S_ACK_IN - get masters response to status send
;*********************************************************************

DO_S_ACK_IN     LDAB    #CNTRL                  ;#$0000 - reset state
		STAB    KB_STATE
		LDAA    #$001B                  ;set wake up, rx, tx
		STAA    SCI_TR_CS
		RTI

;*****************************************************************
;* DO_RCV - got anything to send? If so ACK
;*****************************************************************

DO_RCV          LDAA    DATA_AVAIL          ;get data available byte
		BNE     DO_RCV_0            ;if there is some, then ACK
		LDAA    NM_NACK+KB_NODE     ;#$00C1 - else nack
		BRA     DO_RCV_1
DO_RCV_0        LDAA    NM_ACK+KB_NODE      ;#$0091 - ack
DO_RCV_1        JSR     NET_XMIT
		BCS     DO_RCV_EXIT
		JSR     XMIT_CLEANUP
DO_RCV_EXIT     RTI

;******************************************************************
;* DO_CLR - send a data byte (in a packet)
;******************************************************************

DO_CLR          LDAA    #$000F          ;set up TX int, disable RX
		STAA    SCI_TR_CS
		LDAA    #LEN_HI_OUT     ;#$0008- next state
		STAA    KB_STATE
		LDAA    #$0001          ;xmitting = TRUE
		STAA    XMITTING
		LDAA    #NM_SEND        ;#$00B1 - send cmd to master
		STAA    SCI_TX          ;send it
		RTI

;****************************************************************
;* DO_LENHI_OUT
;****************************************************************

DO_LENHI_OUT    LDAA    #$0000          ;data length hi
		STAA    SCI_TX          ;send it
		LDAA    #LEN_LO_OUT     ;#$0009 - next state
		STAA    KB_STATE
		RTI

;*****************************************************************
;* DO_LENLO_OUT
;*****************************************************************

DO_LENLO_OUT    LDAA    #$0001          ;data length low
		STAA    SCI_TX          ;send it
		LDAA    #DATA_OUT       ;#$000A - next state
		STAA    KB_STATE
		RTI

;******************************************************************
;* DO_DATA_OUT
;******************************************************************

DO_DATA_OUT     LDAA    CUR_DATA        ;get data
		STAA    CHECKSUM        ;save as checksum
		STAA    SCI_TX          ;send it
		LDAA    #CS_OUT         ;#$000B - next state
		STAA    KB_STATE
		RTI

;****************************************************************
;* DO_CS_OUT
;****************************************************************

DO_CS_OUT       LDAA    CHECKSUM        ;get checksum
		STAA    SCI_TX          ;send it
		LDAA    SCI_TR_CS       ;reset any pending ints
		LDAA    SCI_RX
		LDAA    #$001B          ;set wakeup bit, enable tx rx
		STAA    SCI_TR_CS       ;go to sleep
		LDAA    #$0000          ;xmitting=FALSE
		STAA    XMITTING
		LDAA    #ACK_IN         ;#$000C - next state
		STAA    KB_STATE
		RTI

;******************************************************************
;* DO_ACK_IN get masters response from data send
;******************************************************************

DO_ACK_IN       CMPA    #MN_ACK         ;#$0021 - did we get acked?
		BNE     DAI_NACK        ;no-skip
		CLR     DATA_AVAIL      ;yes, clear data ready byte
DAI_NACK        LDAB    #CNTRL          ;#$0000 - reset state
		STAB    KB_STATE
		LDAA    #$001B          ;set wakeup bit
		STAA    SCI_TR_CS       ;go to sleep
		RTI

ROM_STATUS_MSG  equ     $
		db      NM_STATUS+KB_NODE       ;$81 status.OR.address
		db      $01                     ;max msg len low
		db      $00                     ;max msg len hi
		db      $00                     ;xmit code
		db      $00                     ;status flags

;********************************************************************
;* NET_XMIT - send byte in A out over the net
;*            Returns with carry set if error, and carry clear if OK
;********************************************************************

NET_XMIT        PSHX                    ;save X
		LDX     #$0016          ;load retry count
XMIT_LOOP       DEX                     ;dec retrys
		BEQ     XMIT_TIMEOUT    ;skip if ran out
		LDAB    SCI_TR_CS       ;get status
		ANDB    #TDRE_MASK      ;#$0020 - test txrdy bit
		BEQ     XMIT_LOOP       ;not ready, loop
		STAA    SCI_TX          ;transmit byte
		CLC                     ;clear carry
		BRA     XMIT_EXIT       ;skip
XMIT_TIMEOUT    JSR     TO_ERROR
		SEC                     ;set carry
XMIT_EXIT       PULX                    ;restore X
		RTS

;*******************************************************************
;* XMIT_CLEANUP - the net byte we just sent will be echoed back
;*                to us on the shared data line, so just wait for
;*                it and read it now.
;*******************************************************************

XMIT_CLEANUP    PSHX                    ;save X
		LDX     #$0022          ;load retry count
XCU_0           DEX                     ;dec retries
		BEQ     XCU_TO          ;skip if we ran out
		LDAB    SCI_TR_CS       ;read SCI status
		ANDB    #TDRE_MASK      ;#$0020-ready to xmit?
		BEQ     XCU_0           ;no, loop until it is
		LDAB    SCI_RX          ;yes, read rcv reg to clear
XCU_1           DEX                     ;dec retries
		BEQ     XCU_TO          ;skip if we ran out
		LDAB    SCI_TR_CS       ;get SCI status
		BPL     XCU_1           ;loop until RXRDY
		LDAB    SCI_RX          ;read the echoed byte
		PULX                    ;restore X
		RTS                     ;return

XCU_TO          BSR     TO_ERROR
		PULX
		RTS

;*******************************************************************
;* TO_ERROR - timeout error
;*******************************************************************

TO_ERROR        LDAB    SCI_TR_CS       ;read status to clear any pending int
		LDAB    SCI_RX          ;read rcv register
		LDAB    #$001B          ;set wakeup bit
		STAB    SCI_TR_CS       ;go to sleep
		LDAB    #CNTRL          ;#$0000 - reset state
		STAB    KB_STATE
		RTS

;*******************************************************************
;* UNUSED SPACE
;*******************************************************************

		dw      $FFFF,$FFFF,$FFFF,$FFFF
		dw      $FFFF,$FFFF
		db      $FF

;******************************************************************
;* DUMMY - interrupt return for any ints we are not handling
;******************************************************************

DUMMY           RTI

;******************************************************************
;* INTERRUPT VECTORS
;******************************************************************

		dw      MAC             ;serial I/O interrupt
		dw      DUMMY
		dw      TIMER_INT
		dw      DUMMY
		dw      DUMMY
		dw      DUMMY
		dw      DUMMY
RST_INT_ADDR    dw      START           ;reset interrupt

;*************************************************************************
;* MISC
;*************************************************************************

BUFFER_MAX      EQU     0x14            ;max # of chars in buffer
NUM_ROWS        EQU     0x0D            ;number of keyboard scan rows

;*************************************************************************
;* RAM - 80H to FFH
;*************************************************************************

TEMP_VAR_0      EQU     0x82
TEMP_VAR_1      EQU     0x83
TEMP_VAR_2      EQU     0x84
TEMP_VAR_3      EQU     0x85
TMP_BIT_CNTR    EQU     0x86            ;work var for column bit count loop
NUM_BITS        EQU     0x87            ;counter for # of bits in column scan
KEY_CODE        EQU     0x88            ;storage for key value before ASCII

MOD_BYTE        EQU     0x8B            ;modifier byte for tbl lookup
ETBL_PTR        EQU     0x8C            ;ptr to table of key enable bits
TMP_SCAN_RES    EQU     0x8E            ;scan result holding place
TMP_ROW         EQU     0x8F            ;temp row holding place

SCAN_AND_0      EQU     0x90            ;scan code after AND with ~enable
SCAN_AND_1      EQU     0x91            ;scan code after AND with enable
TMP_RET_ADDR    EQU     0x92            ;holding place for return address

SEQ_ASCII       EQU     0x94            ;work area for debounce ret value
		EQU     0x95
SEQUENCE_NUM    EQU     0x96            ;key reading sequence number
SEQUENCE_SCAN   EQU     0x97            ;key holder for debouncer
DEBOUNCE_TIME   EQU     0x98            ;delay time dest for debounce
ENABLE_TBL      EQU     0x99            ;table of key enable values (13 bytes)

		EQU     0xA6

REPEAT_DISABLE  EQU     0xA8            ;flag for disabling repeat
TMP_KB_BYTE     EQU     0xA9            ;used for arrow key multiplex
NEW_KB_BYTE     EQU     0xAA            ;byte from kb to be buffered
RST_CNTDOWN     EQU     0xAB            ;# timer timeouts before restart
REPEAT_STATE    EQU     0xAC            ;repeating keys state
REPEAT_CHAR     EQU     0xAD            ;the current repeating character
REP_STATE1_TIME EQU     0xAE            ;word timer for repeat keys state 1
REP_STATE2_TIME EQU     0xB0            ;word timer for repeat keys state 2
RST_CNTUP       EQU     0xB2            ;word, # timeouts recorded
BUFFER_BASE     EQU     0xB4            ;bottom of KB buffer (20 bytes)

BUF_EMPTY_INDEX EQU     0xC8            ;index to next byte to remove
BUF_FILL_INDEX  EQU     0xC9            ;index to next byte to fill
BUF_HAS_DATA    EQU     0xCA            ;true if there is data in buffer
KB_STATE        EQU     0xCB            ;state byte for keyboard
NUM_X_BYTES     EQU     0xCC            ;word, number of bytes to xmit
CHECKSUM        EQU     0xCE            ;working checksum byte
STAT_OFFSET     EQU     0xCF            ;current offset into status tbl
XMITTING        EQU     0xD0            ;boolean TRUE if tx int active
STACK_TOP       EQU     0xF7            ;stack init
DATA_AVAIL      EQU     0xF8            ;flag if data ready to send
CUR_DATA        EQU     0xF9            ;current data to send

		end

;***************************************************************************
